// Battle(Force) Simulator 
// Input: Two force objects assigned to fight each other this turn, 
//        planet based combat modifiers(e.g. Uncoordinated attack)
// Process: Aerospace Combat, Ground Combat.
// Output: Updated force objects, forced withdrawal of any force(if any).

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

#include "Force.h"

// Roll 2d6
int roll2D6() {
	int die1,die2;
	die1 = (rand()%6)+1;
	die2 = (rand()%6)+1;
	return die1 + die2;
}

// Calculate combat modifier for Force a
int calcCombatMod(Force a, Force b, int combatMod) {

	// Set initial combat modifier based on external modifiers 
	int output = combatMod;

	// Increase modifier if LR of Force a higher than LR of Force b
	if(a.getLeaderRating() > b.getLeaderRating()) output += a.getLeaderRating() - b.getLeaderRating();
	
	// Calculate modifier based on LR of Force a and total aero and ground rating in Force a
	int totalRating = a.getAeroRating() + a.getGroundRating();
	if( (totalRating % 500 == 0) && (totalRating/500)>a.getLeaderRating() ) {
		output -= totalRating/500 - a.getLeaderRating();
	}
	if( (totalRating % 500 != 0) && (totalRating/500 + 1) > a.getLeaderRating() ) {
		output -= (totalRating/500 + 1) - a.getLeaderRating();
	}
	
	return output;

}

// Calculate fatigue modifier based on fatigue point fp
float calcFatigueMod(float fp) {
	
	float mod = 0.00;
	switch((int)fp) {

		case 3: mod = 0.05;
				break;
		case 4: mod = 0.15;
				break;
		case 5: mod = 0.30;
				break;
		case 6: mod = 0.50;
				break;
		case 7: mod = 0.75;
				break;
		case 8: mod = 1.00;
				break;

	}
	return mod;

}

// Calculate modified force strength based on force rating of a force
float calcModForce (Force a) {
	
	float mod = 0.00;
	switch(a.getRating()) {

		case 1: mod = 0.80; break;
		case 2: mod = 1.00; break;
		case 3: mod = 1.50; break;
		case 4: mod = 2.00; 
				int excess = ((int)a.getExp() - 30) / 5;
				mod += (float)excess * 0.10;
				break;

	}
	return mod;

}

/*
// Calculate modifier for aero rating based on force's Faction Battle Tech Level
float calcModTechAero (Force a) {
	
	float mod = 0.00;
	if( a.getFaction().getBTechLevel() >= 550 )
		mod = 0.40;
	else if( a.getFaction().getBTechLevel() >= 400 )
		mod = 0.30;
	else if( a.getFaction().getBTechLevel() >= 250 )
		mod = 0.20;
	else if( a.getFaction().getBTechLevel() >= 50 )
		mod = 0.10;
	return mod;

}

// Calculate modifier for ground rating based on force's Faction Battle Tech Level
float calcModTechGround (Force a) {
	
	float mod = 0.00;
	if( a.getFaction().getBTechLevel() >= 500 )
		mod = 0.40;
	else if( a.getFaction().getBTechLevel() >= 350 )
		mod = 0.30;
	else if( a.getFaction().getBTechLevel() >= 200 )
		mod = 0.20;
	else if( a.getFaction().getBTechLevel() >= 100 )
		mod = 0.10;
	return mod;

}
*/

// Calculate damage percentage based on combat roll
float calcDmgPer(int roll) {

	float dmg = 0.00;
	switch(roll) {

		case 1: dmg = 0.050; break;
		case 2: dmg = 0.075; break;
		case 3: dmg = 0.100; break;
		case 4: dmg = 0.125; break;
		case 5: dmg = 0.150; break;
		case 6: dmg = 0.175; break;
		case 7: dmg = 0.200; break;
		case 8: dmg = 0.225; break;
		case 9: dmg = 0.250; break;
		case 10: dmg = 0.275; break;
		case 11: dmg = 0.300; break;
		case 12: dmg = 0.325; break;
		case 13: dmg = 0.350; break;
		case 14: dmg = 0.375; break;
		default: if(roll<1) dmg = 0.025;
				 else if(roll>14) dmg = 0.400;

	}
	return dmg;

}

// Process Aerospace Battle
// Force a should belong to faction attacking planet
// Force b should belong to planet's controlling faction
// Return (1,0) if Force a forced to surrender
// Return (2,0) if Force b forced to surrender
// Return (3,d) if Force a takes d excess damage
// Return (4,d) if Force b takes d excess damage
// Return (0,0) otherwise
int* aeroBattle(Force &a, Force &b) {
	
	int *output = new int[2];
	output[0] = 0;
	output[1] = 0;
	// Set initial values
	float aRating = (float)a.getAeroRating();
	float bRating = (float)b.getAeroRating();
	float aMod = 1.00, bMod = 1.00;

	// Calculate rating modifier based on supply
	aMod -= (float)a.getNumTurnsWOSupply() * 0.10;
	bMod -= (float)b.getNumTurnsWOSupply() * 0.10;

	// Calculate rating modifier based on fatigue
	aMod -= calcFatigueMod(a.getFatiguePts()); if(aMod<=0.00) {output[0] = 1; return output;}
	bMod -= calcFatigueMod(b.getFatiguePts()); if(bMod<=0.00) {output[0] = 2; return output;}

	// Calculate modified ratings based on supply, fatigue, force rating aNd finally Faction Battle Tech Level modifier
	aRating *= aMod;
	aRating *= calcModForce(a);
	//aRating *= calcModTechAero(a);
	bRating *= bMod;
	bRating *= calcModForce(b);
	//bRating *= calcModTechAero(b);
	
	// Calculate final combat modifiers
	int aCMod = calcCombatMod(a,b,a.getCombatMod());
	int bCMod = calcCombatMod(b,a,b.getCombatMod());
	
	// Calculate modified combat rolls
	int aRoll = roll2D6() + aCMod;
	int bRoll = roll2D6() + bCMod;
	
	// Calculate damage taken by Force a and b
	float aDmg = bRating * calcDmgPer(bRoll);
	float bDmg = aRating * calcDmgPer(aRoll);

	// Calculate final rating for forces
	cout<<"aAeroRoll: "<<aRoll<<" aAeroDmg: "<<aDmg<<endl;
	cout<<"bAeroRoll: "<<bRoll<<" bAeroDmg: "<<bDmg<<endl;
	a.setAeroRating((int)(a.getAeroRating() - aDmg));
	if(a.getAeroRating() < 0) {
		output[0] = 3;
		output[1] = -a.getAeroRating();
		a.setAeroRating(0);
	}
	b.setAeroRating((int)(b.getAeroRating() - bDmg));
	if(b.getAeroRating() < 0) {
		output[0] = 4;
		output[1] = -b.getAeroRating();
		b.setAeroRating(0);
	}

	return output;

}

// Process Ground Battle
// Force a should belong to faction attacking planet
// Force b should belong to planet's controlling faction
// Return (1,0) if Force a forced to surrender
// Return (2,0) if Force b forced to surrender
// Return (3,d) if Force a takes d excess damage
// Return (4,d) if Force b takes d excess damage
// Return (0,0) otherwise
int* groundBattle(Force &a, Force &b, bool airSupport) {

	int *output = new int[2];
	output[0] = 0;
	output[1] = 0;
	// Set initial values
	float aRating = (float)a.getGroundRating();
	float bRating = (float)b.getGroundRating();
	float aMod = 1.00, bMod = 1.00;

	// Add air support rating(if any)
	if(airSupport) {
		if(a.getAeroRating()==0) {// Add air support for Force b
			bRating += (float)b.getAeroRating() * 2;
		}
		else if(b.getAeroRating()==0) {// Add air support for Force a
			aRating += (float)a.getAeroRating() * 2;
		}
	}

	// Calculate rating modifier based on supply
	aMod -= (float)a.getNumTurnsWOSupply() * 0.10;
	bMod -= (float)b.getNumTurnsWOSupply() * 0.10;

	// Calculate rating modifier based on fatigue
	aMod -= calcFatigueMod(a.getFatiguePts()); if(aMod<=0.00) {output[0] = 1; return output;}
	bMod -= calcFatigueMod(b.getFatiguePts()); if(bMod<=0.00) {output[0] = 2; return output;}
	
	// Calculate modified ratings based on supply, fatigue, force rating aNd finally Faction Battle Tech Level modifier
	aRating *= aMod;
	aRating *= calcModForce(a);
	//aRating *= calcModTechGround(a);
	bRating *= bMod;
	bRating *= calcModForce(b);
	//bRating *= calcModTechGround(b);

	// Calculate final combat modifiers
	int aCMod = calcCombatMod(a,b,a.getCombatMod());
	int bCMod = calcCombatMod(b,a,b.getCombatMod());

	// Calculate modified combat rolls
	int aRoll = roll2D6() + aCMod;
	int bRoll = roll2D6() + bCMod;
	
	// Calculate damage taken by Force a and b
	float aDmg = bRating * calcDmgPer(bRoll);
	float bDmg = aRating * calcDmgPer(aRoll);

	// Calculate final rating for forces
	cout<<"aGroundRoll: "<<aRoll<<" aGroundDmg: "<<aDmg<<endl;
	cout<<"bGroundRoll: "<<bRoll<<" bGroundDmg: "<<bDmg<<endl;
	a.setGroundRating((int)(a.getGroundRating() - aDmg));
	if(a.getGroundRating() < 0) {
		output[0] = 3;
		output[1] = -a.getGroundRating();
		a.setGroundRating(0);
	}
	b.setGroundRating((int)(b.getGroundRating() - bDmg));
	if(b.getGroundRating() < 0) {
		output[0] = 4;
		output[1] = -b.getGroundRating();
		b.setGroundRating(0);
	}
	return output;

}

// Process aero than ground battle, followed by updating supply need for both Forces
// Force a should belong to faction attacking planet
// Force b should belong to planet's controlling faction
// Return (1,A,B,C,D) if Force a forced to retreat
// Return (2,A,B,C,D) if Force b forced to retreat
// Return (3,A,B,C,D) if Force a forced to surrender
// Return (4,A,B,C,D) if Force b forced to surrender
// Return (0,A,B,C,D) otherwise
// A is Force a excess aero damage, B is Force a excess ground damage
// C is Force b excess aero damage, D is Force b excess ground damage
//
// CASE: If both sides go below surrender level or forced to surrender
// SOLUTION: The force that does not control the planet surrenders to the other force
int* battle(Force &a, Force &b, bool airSupport) {

	int* output = new int[5];
	int* result = new int[2];
	for(int i=0;i<5;i++) {
		output[i] = 0;
	}

	// Process Aerospace Battle phase
	result = aeroBattle(a, b);
	if(result[0] == 1) output[0] = 3;
	if(result[0] == 2) output[0] = 4;
	if(result[0] == 3) output[1] = result[1];
	if(result[0] == 4) output[3] = result[1];

	// Process Ground Battle phase
	result = groundBattle(a, b, airSupport);
	if(result[0] == 1) output[0] = 3;
	if(result[0] == 2) output[0] = 4;
	if(result[0] == 3) output[2] = result[1];
	if(result[0] == 4) output[4] = result[1];

	// Update supply needs of both forces
	a.setSupplyNeed();
	b.setSupplyNeed();

	// Check if a force has exceeded it's Surrender Level
	if(output[0] == 0) {
	
		if(b.checkSurrenderLimit()) output[0] = 4; 
		if(a.checkSurrenderLimit()) output[0] = 3; 
		
	}

	// Check if a force is forced to withdraw due to being outnumbered 3:1 or more
	if(output[0] == 0) {

		if( ( ( a.getAeroRating() + a.getGroundRating() ) * 3 ) < ( b.getAeroRating() + b.getGroundRating() ) )
			output[0] = 1;
		else if( ( ( b.getAeroRating() + b.getGroundRating() ) * 3 ) < ( a.getAeroRating() + a.getGroundRating() ) )
			output[0] = 2;
	
	}
	delete[] result;
	return output;

}

int main() {
	// Seed random number generator
	srand((unsigned)time(0));

	int* output = new int[5];

	Force A(1000,1700,1000,1700,10,9,0,0);
	Force B(1000,1500,700,800,34,9,0,0);
	
	cout<<A;
	cout<<B;
	
	output = battle(A,B,true);
	cout<<A;
	cout<<B;
	for(int i=0;i<5;i++)
		cout<<output[i]<<",";
	cout<<endl;
	output = battle(A,B,true);
	cout<<A;
	cout<<B;
	for(int i=0;i<5;i++)
		cout<<output[i]<<",";
	cout<<endl;
	output = battle(A,B,true);
		cout<<A;
	cout<<B;
	for(int i=0;i<5;i++)
		cout<<output[i]<<",";
	cout<<endl;
	output = battle(A,B,true);
		cout<<A;
	cout<<B;
	for(int i=0;i<5;i++)
		cout<<output[i]<<",";
	cout<<endl;
	output = battle(A,B,true);
		cout<<A;
	cout<<B;
	for(int i=0;i<5;i++)
		cout<<output[i]<<",";
	cout<<endl;
	int test;
	cin>>test;
	delete output;
	return 0;
}